Subnet 是 VPC 內的一個 IP 位址範圍,它位於特定的可用區 (Availability Zone) 內。Subnet 用於將不同類型的資源組織到不同的網路分區中,以增加網路安全性和可用性。每個 Subnet 可以關聯一個或多個 Route Table。
本篇是實作常用的 AWS Subnet 服務之 Terraform 模組,並且會使用到 YAML 資料結構來定義模組的內容,完整的專案程式碼分享在我的 Github 上。
./configs/subnet/my_subnets.yaml
與模組 my_subnets
的放置位置:├── configs
│ ├── subnet
│ │ └── my-subnets.yaml
│ └── vpc
│ └── my-vpcs.yaml
├── example.tfvars
├── main.tf
├── modules
│ ├── my_subnets
│ │ ├── outputs.tf
│ │ ├── provider.tf
│ │ ├── subnet.tf
│ │ └── variables.tf
│ └── my_vpc
└── variables.tf
./configs/subnet/my-subnets.yaml
內容來定義 Subnet 需要用到的屬性:
cidr
: The CIDR of the subnetname
: The name of the subnetzone
: The zone 0f the subnetsubnets:
# application
- cidr: 10.2.4.0/24
name: my-application-ap-northeast-1a
zone: ap-northeast-1a
- cidr: 10.2.5.0/24
name: my-application-ap-northeast-1c
zone: ap-northeast-1c
- cidr: 10.2.6.0/24
name: my-application-ap-northeast-1d
zone: ap-northeast-1d
# intra
- cidr: 10.2.8.0/24
name: my-intra-ap-northeast-1a
zone: ap-northeast-1a
- cidr: 10.2.9.0/24
name: my-intra-ap-northeast-1c
zone: ap-northeast-1c
- cidr: 10.2.10.0/24
name: my-intra-ap-northeast-1d
zone: ap-northeast-1d
# public
- cidr: 10.2.0.0/24
name: my-public-ap-northeast-1a
zone: ap-northeast-1a
- cidr: 10.2.1.0/24
name: my-public-ap-northeast-1c
zone: ap-northeast-1c
- cidr: 10.2.2.0/24
name: my-public-ap-northeast-1d
zone: ap-northeast-1d
# persistence
- cidr: 10.2.16.0/24
name: my-persistence-ap-northeast-1a
zone: ap-northeast-1a
- cidr: 10.2.17.0/24
name: my-persistence-ap-northeast-1c
zone: ap-northeast-1c
- cidr: 10.2.18.0/24
name: my-persistence-ap-northeast-1d
zone: ap-northeast-1d
# nat
- cidr: 10.2.3.0/24
name: my-nat-server
zone: ap-northeast-1d
my_subnets
模組./modules/my_subnets/outputs.tf
:output "subnets" {
value = aws_subnet.subnets
}
./modules/my_subnets/provider.tf
:provider "aws" {
region = var.aws_region
profile = var.aws_profile
}
./modules/my_subnets/variables.tf
:
subnet_path
為傳入的 my-subnets.yaml
設定檔路徑位址,透過 yamldecode
取出 key 值 subnets
的 value 為一 list 物件
vpc_id
會透過 my_vpc
模組建立的 VPC 來傳入其 id
locals {
subnets = yamldecode(file("${var.subnet_path}"))["subnets"]
}
variable "aws_region" {
description = "AWS region"
default = "ap-northeast-1"
}
variable "aws_profile" {
description = "AWS profile"
default = ""
}
variable "project_name" {
type = string
description = "Project name"
default = ""
}
variable "department_name" {
type = string
description = "Department name"
default = "SRE"
}
variable "vpc_id" {
type = string
description = "The id of VPC"
}
variable "subnet_path" {
type = string
description = "Subnet path"
default = ""
}
./modules/my_subnets/subnet.tf
:for_each
來迭代 locals.subnets
以 name
為 key 值建立 map 物件
resource "aws_subnet" "subnets" {
for_each = { for r in local.subnets : r.name => r }
assign_ipv6_address_on_creation = lookup(each.value, "assign_ipv6_address_on_creation", false)
availability_zone = each.value.zone
cidr_block = each.value.cidr
ipv6_cidr_block = lookup(each.value, "ipv6_cidr", null)
customer_owned_ipv4_pool = ""
map_customer_owned_ip_on_launch = false
map_public_ip_on_launch = lookup(each.value, "map_public_ip_on_launch", false)
outpost_arn = ""
tags = {
Department = var.department_name
Env = var.environment
Name = each.value.name
Project = lookup(each.value, "project", var.project_name)
"kubernetes.io/role/elb" = lookup(each.value, "k8s_elb", false) ? "1" : null
"kubernetes.io/role/internal-elb" = lookup(each.value, "k8s_internal_elb", false) ? "1" : null
"karpenter.sh/discovery" = lookup(each.value, "karpenter_discovery", null)
}
tags_all = {
Department = var.department_name
Env = var.environment
Name = each.value.name
Project = var.project_name
"kubernetes.io/role/elb" = lookup(each.value, "k8s_elb", false) ? "1" : null
"kubernetes.io/role/internal-elb" = lookup(each.value, "karpenter_discovery", null)
}
vpc_id = var.vpc_id
depends_on = [
var.vpc_id
]
}
example.tfvars
:aws_region="ap-northeast-1"
aws_profile="<YOUR_PROFILE>"
project_name="example"
department_name="SRE"
main.tf
:terraform {
required_providers {
aws = {
version = "5.15.0"
}
}
backend "s3" {
bucket = "<YOUR_S3_BUCKET_NAME>"
dynamodb_table = "<YOUR_DYNAMODB_TABLE_NAME>"
key = "terraform.tfstate"
region = "ap-northeast-1"
shared_credentials_file = "~/.aws/config"
profile = "<YOUR_PROFILE>"
}
}
# vpc
module "vpc" {
aws_profile = var.aws_profile
aws_region = var.aws_region
department_name = var.department_name
project_name = var.project_name
vpc_path = "./configs/vpc/my-vpcs.yaml"
source = "./modules/my_vpc"
}
# subnet
module "subnet" {
aws_profile = var.aws_profile
aws_region = var.aws_region
department_name = var.department_name
project_name = var.project_name
vpc_id = module.vpc.my_vpcs["my-vpc"].id
subnet_path = "./configs/subnet/my-subnets.yaml"
source = "./modules/my_subnets"
}
於專案目錄下執行 terraform init && terraform plan --out .plan -var-file=example.tfvars
來確認一下結果:
Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the
following symbols:
+ create
Terraform will perform the following actions:
# module.subnet.aws_subnet.subnets["my-application-ap-northeast-1a"] will be created
+ resource "aws_subnet" "subnets" {
+ arn = (known after apply)
+ assign_ipv6_address_on_creation = false
+ availability_zone = "ap-northeast-1a"
+ availability_zone_id = (known after apply)
+ cidr_block = "10.2.4.0/24"
+ enable_dns64 = false
+ enable_resource_name_dns_a_record_on_launch = false
+ enable_resource_name_dns_aaaa_record_on_launch = false
+ id = (known after apply)
+ ipv6_cidr_block_association_id = (known after apply)
+ ipv6_native = false
+ map_customer_owned_ip_on_launch = false
+ map_public_ip_on_launch = false
+ owner_id = (known after apply)
+ private_dns_hostname_type_on_launch = (known after apply)
+ tags = {
+ "Department" = "SRE"
+ "Name" = "my-application-ap-northeast-1a"
+ "Project" = "example"
}
+ tags_all = {
+ "Department" = "SRE"
+ "Name" = "my-application-ap-northeast-1a"
+ "Project" = "example"
}
+ vpc_id = (known after apply)
}
# module.subnet.aws_subnet.subnets["my-application-ap-northeast-1c"] will be created
+ resource "aws_subnet" "subnets" {
+ arn = (known after apply)
+ assign_ipv6_address_on_creation = false
+ availability_zone = "ap-northeast-1c"
+ availability_zone_id = (known after apply)
+ cidr_block = "10.2.5.0/24"
+ enable_dns64 = false
+ enable_resource_name_dns_a_record_on_launch = false
+ enable_resource_name_dns_aaaa_record_on_launch = false
+ id = (known after apply)
+ ipv6_cidr_block_association_id = (known after apply)
+ ipv6_native = false
+ map_customer_owned_ip_on_launch = false
+ map_public_ip_on_launch = false
+ owner_id = (known after apply)
+ private_dns_hostname_type_on_launch = (known after apply)
+ tags = {
+ "Department" = "SRE"
+ "Name" = "my-application-ap-northeast-1c"
+ "Project" = "example"
}
+ tags_all = {
+ "Department" = "SRE"
+ "Name" = "my-application-ap-northeast-1c"
+ "Project" = "example"
}
+ vpc_id = (known after apply)
}
# module.subnet.aws_subnet.subnets["my-application-ap-northeast-1d"] will be created
+ resource "aws_subnet" "subnets" {
+ arn = (known after apply)
+ assign_ipv6_address_on_creation = false
+ availability_zone = "ap-northeast-1d"
+ availability_zone_id = (known after apply)
+ cidr_block = "10.2.6.0/24"
+ enable_dns64 = false
+ enable_resource_name_dns_a_record_on_launch = false
+ enable_resource_name_dns_aaaa_record_on_launch = false
+ id = (known after apply)
+ ipv6_cidr_block_association_id = (known after apply)
+ ipv6_native = false
+ map_customer_owned_ip_on_launch = false
+ map_public_ip_on_launch = false
+ owner_id = (known after apply)
+ private_dns_hostname_type_on_launch = (known after apply)
+ tags = {
+ "Department" = "SRE"
+ "Name" = "my-application-ap-northeast-1d"
+ "Project" = "example"
}
+ tags_all = {
+ "Department" = "SRE"
+ "Name" = "my-application-ap-northeast-1d"
+ "Project" = "example"
}
+ vpc_id = (known after apply)
}
# module.subnet.aws_subnet.subnets["my-intra-ap-northeast-1a"] will be created
+ resource "aws_subnet" "subnets" {
+ arn = (known after apply)
+ assign_ipv6_address_on_creation = false
+ availability_zone = "ap-northeast-1a"
+ availability_zone_id = (known after apply)
+ cidr_block = "10.2.8.0/24"
+ enable_dns64 = false
+ enable_resource_name_dns_a_record_on_launch = false
+ enable_resource_name_dns_aaaa_record_on_launch = false
+ id = (known after apply)
+ ipv6_cidr_block_association_id = (known after apply)
+ ipv6_native = false
+ map_customer_owned_ip_on_launch = false
+ map_public_ip_on_launch = false
+ owner_id = (known after apply)
+ private_dns_hostname_type_on_launch = (known after apply)
+ tags = {
+ "Department" = "SRE"
+ "Name" = "my-intra-ap-northeast-1a"
+ "Project" = "example"
}
+ tags_all = {
+ "Department" = "SRE"
+ "Name" = "my-intra-ap-northeast-1a"
+ "Project" = "example"
}
+ vpc_id = (known after apply)
}
# module.subnet.aws_subnet.subnets["my-intra-ap-northeast-1c"] will be created
+ resource "aws_subnet" "subnets" {
+ arn = (known after apply)
+ assign_ipv6_address_on_creation = false
+ availability_zone = "ap-northeast-1c"
+ availability_zone_id = (known after apply)
+ cidr_block = "10.2.9.0/24"
+ enable_dns64 = false
+ enable_resource_name_dns_a_record_on_launch = false
+ enable_resource_name_dns_aaaa_record_on_launch = false
+ id = (known after apply)
+ ipv6_cidr_block_association_id = (known after apply)
+ ipv6_native = false
+ map_customer_owned_ip_on_launch = false
+ map_public_ip_on_launch = false
+ owner_id = (known after apply)
+ private_dns_hostname_type_on_launch = (known after apply)
+ tags = {
+ "Department" = "SRE"
+ "Name" = "my-intra-ap-northeast-1c"
+ "Project" = "example"
}
+ tags_all = {
+ "Department" = "SRE"
+ "Name" = "my-intra-ap-northeast-1c"
+ "Project" = "example"
}
+ vpc_id = (known after apply)
}
# module.subnet.aws_subnet.subnets["my-intra-ap-northeast-1d"] will be created
+ resource "aws_subnet" "subnets" {
+ arn = (known after apply)
+ assign_ipv6_address_on_creation = false
+ availability_zone = "ap-northeast-1d"
+ availability_zone_id = (known after apply)
+ cidr_block = "10.2.10.0/24"
+ enable_dns64 = false
+ enable_resource_name_dns_a_record_on_launch = false
+ enable_resource_name_dns_aaaa_record_on_launch = false
+ id = (known after apply)
+ ipv6_cidr_block_association_id = (known after apply)
+ ipv6_native = false
+ map_customer_owned_ip_on_launch = false
+ map_public_ip_on_launch = false
+ owner_id = (known after apply)
+ private_dns_hostname_type_on_launch = (known after apply)
+ tags = {
+ "Department" = "SRE"
+ "Name" = "my-intra-ap-northeast-1d"
+ "Project" = "example"
}
+ tags_all = {
+ "Department" = "SRE"
+ "Name" = "my-intra-ap-northeast-1d"
+ "Project" = "example"
}
+ vpc_id = (known after apply)
}
# module.subnet.aws_subnet.subnets["my-nat-server"] will be created
+ resource "aws_subnet" "subnets" {
+ arn = (known after apply)
+ assign_ipv6_address_on_creation = false
+ availability_zone = "ap-northeast-1d"
+ availability_zone_id = (known after apply)
+ cidr_block = "10.2.3.0/24"
+ enable_dns64 = false
+ enable_resource_name_dns_a_record_on_launch = false
+ enable_resource_name_dns_aaaa_record_on_launch = false
+ id = (known after apply)
+ ipv6_cidr_block_association_id = (known after apply)
+ ipv6_native = false
+ map_customer_owned_ip_on_launch = false
+ map_public_ip_on_launch = false
+ owner_id = (known after apply)
+ private_dns_hostname_type_on_launch = (known after apply)
+ tags = {
+ "Department" = "SRE"
+ "Name" = "my-nat-server"
+ "Project" = "example"
}
+ tags_all = {
+ "Department" = "SRE"
+ "Name" = "my-nat-server"
+ "Project" = "example"
}
+ vpc_id = (known after apply)
}
# module.subnet.aws_subnet.subnets["my-persistence-ap-northeast-1a"] will be created
+ resource "aws_subnet" "subnets" {
+ arn = (known after apply)
+ assign_ipv6_address_on_creation = false
+ availability_zone = "ap-northeast-1a"
+ availability_zone_id = (known after apply)
+ cidr_block = "10.2.16.0/24"
+ enable_dns64 = false
+ enable_resource_name_dns_a_record_on_launch = false
+ enable_resource_name_dns_aaaa_record_on_launch = false
+ id = (known after apply)
+ ipv6_cidr_block_association_id = (known after apply)
+ ipv6_native = false
+ map_customer_owned_ip_on_launch = false
+ map_public_ip_on_launch = false
+ owner_id = (known after apply)
+ private_dns_hostname_type_on_launch = (known after apply)
+ tags = {
+ "Department" = "SRE"
+ "Name" = "my-persistence-ap-northeast-1a"
+ "Project" = "example"
}
+ tags_all = {
+ "Department" = "SRE"
+ "Name" = "my-persistence-ap-northeast-1a"
+ "Project" = "example"
}
+ vpc_id = (known after apply)
}
# module.subnet.aws_subnet.subnets["my-persistence-ap-northeast-1c"] will be created
+ resource "aws_subnet" "subnets" {
+ arn = (known after apply)
+ assign_ipv6_address_on_creation = false
+ availability_zone = "ap-northeast-1c"
+ availability_zone_id = (known after apply)
+ cidr_block = "10.2.17.0/24"
+ enable_dns64 = false
+ enable_resource_name_dns_a_record_on_launch = false
+ enable_resource_name_dns_aaaa_record_on_launch = false
+ id = (known after apply)
+ ipv6_cidr_block_association_id = (known after apply)
+ ipv6_native = false
+ map_customer_owned_ip_on_launch = false
+ map_public_ip_on_launch = false
+ owner_id = (known after apply)
+ private_dns_hostname_type_on_launch = (known after apply)
+ tags = {
+ "Department" = "SRE"
+ "Name" = "my-persistence-ap-northeast-1c"
+ "Project" = "example"
}
+ tags_all = {
+ "Department" = "SRE"
+ "Name" = "my-persistence-ap-northeast-1c"
+ "Project" = "example"
}
+ vpc_id = (known after apply)
}
# module.subnet.aws_subnet.subnets["my-persistence-ap-northeast-1d"] will be created
+ resource "aws_subnet" "subnets" {
+ arn = (known after apply)
+ assign_ipv6_address_on_creation = false
+ availability_zone = "ap-northeast-1d"
+ availability_zone_id = (known after apply)
+ cidr_block = "10.2.18.0/24"
+ enable_dns64 = false
+ enable_resource_name_dns_a_record_on_launch = false
+ enable_resource_name_dns_aaaa_record_on_launch = false
+ id = (known after apply)
+ ipv6_cidr_block_association_id = (known after apply)
+ ipv6_native = false
+ map_customer_owned_ip_on_launch = false
+ map_public_ip_on_launch = false
+ owner_id = (known after apply)
+ private_dns_hostname_type_on_launch = (known after apply)
+ tags = {
+ "Department" = "SRE"
+ "Name" = "my-persistence-ap-northeast-1d"
+ "Project" = "example"
}
+ tags_all = {
+ "Department" = "SRE"
+ "Name" = "my-persistence-ap-northeast-1d"
+ "Project" = "example"
}
+ vpc_id = (known after apply)
}
# module.subnet.aws_subnet.subnets["my-public-ap-northeast-1a"] will be created
+ resource "aws_subnet" "subnets" {
+ arn = (known after apply)
+ assign_ipv6_address_on_creation = false
+ availability_zone = "ap-northeast-1a"
+ availability_zone_id = (known after apply)
+ cidr_block = "10.2.0.0/24"
+ enable_dns64 = false
+ enable_resource_name_dns_a_record_on_launch = false
+ enable_resource_name_dns_aaaa_record_on_launch = false
+ id = (known after apply)
+ ipv6_cidr_block_association_id = (known after apply)
+ ipv6_native = false
+ map_customer_owned_ip_on_launch = false
+ map_public_ip_on_launch = false
+ owner_id = (known after apply)
+ private_dns_hostname_type_on_launch = (known after apply)
+ tags = {
+ "Department" = "SRE"
+ "Name" = "my-public-ap-northeast-1a"
+ "Project" = "example"
}
+ tags_all = {
+ "Department" = "SRE"
+ "Name" = "my-public-ap-northeast-1a"
+ "Project" = "example"
}
+ vpc_id = (known after apply)
}
# module.subnet.aws_subnet.subnets["my-public-ap-northeast-1c"] will be created
+ resource "aws_subnet" "subnets" {
+ arn = (known after apply)
+ assign_ipv6_address_on_creation = false
+ availability_zone = "ap-northeast-1c"
+ availability_zone_id = (known after apply)
+ cidr_block = "10.2.1.0/24"
+ enable_dns64 = false
+ enable_resource_name_dns_a_record_on_launch = false
+ enable_resource_name_dns_aaaa_record_on_launch = false
+ id = (known after apply)
+ ipv6_cidr_block_association_id = (known after apply)
+ ipv6_native = false
+ map_customer_owned_ip_on_launch = false
+ map_public_ip_on_launch = false
+ owner_id = (known after apply)
+ private_dns_hostname_type_on_launch = (known after apply)
+ tags = {
+ "Department" = "SRE"
+ "Name" = "my-public-ap-northeast-1c"
+ "Project" = "example"
}
+ tags_all = {
+ "Department" = "SRE"
+ "Name" = "my-public-ap-northeast-1c"
+ "Project" = "example"
}
+ vpc_id = (known after apply)
}
# module.subnet.aws_subnet.subnets["my-public-ap-northeast-1d"] will be created
+ resource "aws_subnet" "subnets" {
+ arn = (known after apply)
+ assign_ipv6_address_on_creation = false
+ availability_zone = "ap-northeast-1d"
+ availability_zone_id = (known after apply)
+ cidr_block = "10.2.2.0/24"
+ enable_dns64 = false
+ enable_resource_name_dns_a_record_on_launch = false
+ enable_resource_name_dns_aaaa_record_on_launch = false
+ id = (known after apply)
+ ipv6_cidr_block_association_id = (known after apply)
+ ipv6_native = false
+ map_customer_owned_ip_on_launch = false
+ map_public_ip_on_launch = false
+ owner_id = (known after apply)
+ private_dns_hostname_type_on_launch = (known after apply)
+ tags = {
+ "Department" = "SRE"
+ "Name" = "my-public-ap-northeast-1d"
+ "Project" = "example"
}
+ tags_all = {
+ "Department" = "SRE"
+ "Name" = "my-public-ap-northeast-1d"
+ "Project" = "example"
}
+ vpc_id = (known after apply)
}
# module.vpc.aws_vpc.my_vpcs["my-vpc"] will be created
+ resource "aws_vpc" "my_vpcs" {
+ arn = (known after apply)
+ assign_generated_ipv6_cidr_block = false
+ cidr_block = "10.2.0.0/16"
+ default_network_acl_id = (known after apply)
+ default_route_table_id = (known after apply)
+ default_security_group_id = (known after apply)
+ dhcp_options_id = (known after apply)
+ enable_dns_hostnames = true
+ enable_dns_support = true
+ enable_network_address_usage_metrics = (known after apply)
+ id = (known after apply)
+ instance_tenancy = "default"
+ ipv6_association_id = (known after apply)
+ ipv6_cidr_block = (known after apply)
+ ipv6_cidr_block_network_border_group = (known after apply)
+ main_route_table_id = (known after apply)
+ owner_id = (known after apply)
+ tags = {
+ "Department" = "SRE"
+ "Name" = "my-vpc"
+ "Project" = "example"
}
+ tags_all = {
+ "Department" = "SRE"
+ "Name" = "my-vpc"
+ "Project" = "example"
}
}
Plan: 14 to add, 0 to change, 0 to destroy.
─────────────────────────────────────────────────────────────────────────────────────
Saved the plan to: .plan
To perform exactly these actions, run the following command to apply:
terraform apply ".plan"
Releasing state lock. This may take a few moments...
下一篇文章將會展示實作 AWS Internet Gateway 之 Terraform 模組。